# Copyright (c) 2011 OpenStack Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from time import time
from traceback import format_exc
from urllib import unquote
from uuid import uuid4
from hashlib import sha1
import hmac
import base64
import zmq,json
from eventlet import Timeout
from swift.common.swob import Response, Request
from swift.common.swob import HTTPBadRequest, HTTPForbidden, HTTPNotFound, \
    HTTPUnauthorized

from swift.common.middleware.acl import clean_acl, parse_acl, referrer_allowed
from swift.common.utils import cache_from_env, get_logger, \
    split_path, config_true_value

import logging
logging.basicConfig(level=logging.INFO,filename="/home/wxt/auth.log",filemode="a")
logger = logging.getLogger(__name__)

def logobj(req):
    import json
    attrs = [attr for attr in dir(req) if not attr.startswith('__')]
    d={}
    for a in attrs:
        t=getattr(req,a)
        if type(t) not in [dict,str]:
            continue
        else:
            if a=="environ":
                td={}
                for tk in t:
                    if type(t[tk]) in [str,tuple,list]:
                        td[tk]=t[tk]
                d[a]=td
            else:
                d[a]=str(t)
    s=json.dumps(d)
    logger.info("===========")
    logger.info(s)
    logger.info("===========")
    return s

class TempAuth(object):
    def __init__(self, app, conf):
        self.app = app
        self.conf = conf
        self.cache={}
        self.context = zmq.Context()
        self.socket = self.context.socket(zmq.REQ)
        self.socket.connect("tcp://localhost:8888")
        self.logger = get_logger(conf, log_route='tempauth')
        self.log_headers = config_true_value(conf.get('log_headers', 'f'))
        self.reseller_prefix = conf.get('reseller_prefix', 'AUTH').strip()
        if self.reseller_prefix and self.reseller_prefix[-1] != '_':
            self.reseller_prefix += '_'
        self.logger.set_statsd_prefix('tempauth.%s' % (
            self.reseller_prefix if self.reseller_prefix else 'NONE',))
        self.auth_prefix = conf.get('auth_prefix', '/auth/')
        if not self.auth_prefix or not self.auth_prefix.strip('/'):
            self.logger.warning('Rewriting invalid auth prefix "%s" to '
                                '"/auth/" (Non-empty auth prefix path '
                                'is required)' % self.auth_prefix)
            self.auth_prefix = '/auth/'
        if self.auth_prefix[0] != '/':
            self.auth_prefix = '/' + self.auth_prefix
        if self.auth_prefix[-1] != '/':
            self.auth_prefix += '/'
        self.token_life = int(conf.get('token_life', 86400))
        self.allow_overrides = config_true_value(
            conf.get('allow_overrides', 't'))
        self.storage_url_scheme = conf.get('storage_url_scheme', 'default')

    def __call__(self, env, start_response):

        logger.info("call __call__:%s"%(env))

        if self.allow_overrides and env.get('swift.authorize_override', False):
            return self.app(env, start_response)
        if env.get('PATH_INFO', '').startswith(self.auth_prefix):
            return self.handle(env, start_response)
        s3 = env.get('HTTP_AUTHORIZATION')
        token = env.get('HTTP_X_AUTH_TOKEN', env.get('HTTP_X_STORAGE_TOKEN'))
        logger.info("hahahahaahah %s"%(token))
        logger.info("hahahahaahah %s"%(self.cache))
        #token=AUTH_tk1b34a4e76f5d43d7965e461417e62e85
        if s3 or (token and token.startswith(self.reseller_prefix)):
            # Note: Empty reseller_prefix will match all tokens.
            #groups = self.get_groups(env, token)
            groups=self.cache[token]
            if groups:
                user = groups and groups.split(',', 1)[0] or ''
                trans_id = env.get('swift.trans_id')
                env['REMOTE_USER'] = groups
                env['swift.authorize'] = self.authorize
                env['swift.clean_acl'] = clean_acl
                if '.reseller_admin' in groups:
                    env['reseller_request'] = True
            else:
                # Unauthorized token
                logger.info("goto __call prex could not get groups")
                if self.reseller_prefix:
                    # Because I know I'm the definitive auth for this token, I
                    # can deny it outright.
                    self.logger.increment('unauthorized')
                    try:
                        vrs, realm, rest = split_path(env['PATH_INFO'],
                                                      2, 3, True)
                    except ValueError:
                        realm = 'unknown'
                    return HTTPUnauthorized(headers={
                        'Www-Authenticate': 'Swift realm="%s"' % realm})(
                            env, start_response)
                elif 'swift.authorize' not in env:
                    env['swift.authorize'] = self.denied_response
        else:
            if self.reseller_prefix:
                try:
                    version, rest = split_path(env.get('PATH_INFO', ''),
                                               1, 2, True)
                except ValueError:
                    version, rest = None, None
                if rest and rest.startswith(self.reseller_prefix):
                    env['swift.authorize'] = self.authorize
                    env['swift.clean_acl'] = clean_acl

                elif 'swift.authorize' not in env:
                    env['swift.authorize'] = self.denied_response
            elif 'swift.authorize' not in env:
                logger.info("goto __call not reseller_prefix")
                env['swift.authorize'] = self.authorize
                env['swift.clean_acl'] = clean_acl

        return self.app(env, start_response)

    def authorize(self, req):
        query={}
        version, account, container, obj = req.split_path(1, 4, True)
        query['version']=version
        query['account']=account
        query['container']=container
        query['obj']=obj
        query['method']=req.method
        query['remote_addr']=req.remote_addr
        query['remote_user']=req.remote_user
        query['user_agent']=req.user_agent
        query['X-Auth-User']=req.environ.get('X-Auth-User')
        query['HTTP_X_AUTH_KEY']=req.environ.get('HTTP_X_AUTH_KEY')
        query['user_info']=""
        s=json.dumps(query)
        self.socket.send(json.dumps(query))
        rsp=self.socket.recv()
        if rsp!="DENY":
            return None
        #logger.info("authorize-info:token:%s,%s,%s,%s"%(version,account,container,obj))

        #req.headers
        #req.remote_user
        #req.environ.get('REMOTE_PORT')
        #req.remote_addr
        #req.user_agent
        #req.referer
        #req.referrer
        #req.params
        #req.path
        #req.path_info
        #req.method

        return self.denied_response(req)

    def denied_response(self, req):
        if req.remote_user:
            self.logger.increment('forbidden')
            return HTTPForbidden(request=req)
        else:
            self.logger.increment('unauthorized')
            return HTTPUnauthorized(request=req)

    def handle(self, env, start_response):
        logger.info("call handle")

        try:
            req = Request(env)

            if self.auth_prefix:
                req.path_info_pop()
            req.bytes_transferred = '-'
            req.client_disconnect = False
            if 'x-storage-token' in req.headers and \
                    'x-auth-token' not in req.headers:
                req.headers['x-auth-token'] = req.headers['x-storage-token']
            return self.handle_request(req)(env, start_response)
        except (Exception, Timeout):
            print "EXCEPTION IN handle: %s: %s" % (format_exc(), env)
            self.logger.increment('errors')
            start_response('500 Server Error',
                           [('Content-Type', 'text/plain')])
            return ['Internal server error.\n']

    def handle_request(self, req):

        logger.info("call handle_request")

        req.start_time = time()
        handler = None
        try:
            version, account, user, _junk = req.split_path(1, 4, True)
        except ValueError:
            self.logger.increment('errors')
            return HTTPNotFound(request=req)
        if version in ('v1', 'v1.0', 'auth'):
            if req.method == 'GET':
                handler = self.handle_get_token
        if not handler:
            self.logger.increment('errors')
            req.response = HTTPBadRequest(request=req)
        else:
            req.response = handler(req)
        return req.response

    def handle_get_token(self, req):
        try:
            pathsegs = split_path(req.path_info, 1, 3, True)
        except ValueError:
            self.logger.increment('errors')
            return HTTPNotFound(request=req)
        if pathsegs[0] == 'v1' and pathsegs[2] == 'auth':
            account = pathsegs[1]
            user = req.headers.get('x-storage-user')

            if not user:
                user = req.headers.get('x-auth-user')
                if not user or ':' not in user:
                    self.logger.increment('token_denied')
                    return HTTPUnauthorized(request=req, headers=
                                            {'Www-Authenticate':
                                             'Swift realm="%s"' % account})
                account2, user = user.split(':', 1)
                if account != account2:
                    self.logger.increment('token_denied')
                    return HTTPUnauthorized(request=req, headers=
                                            {'Www-Authenticate':
                                             'Swift realm="%s"' % account})
            key = req.headers.get('x-storage-pass')
            if not key:
                key = req.headers.get('x-auth-key')
        elif pathsegs[0] in ('auth', 'v1.0'):
            user = req.headers.get('x-auth-user')
            if not user:
                user = req.headers.get('x-storage-user')
            if not user or ':' not in user:
                self.logger.increment('token_denied')
                return HTTPUnauthorized(request=req, headers=
                                        {'Www-Authenticate':
                                         'Swift realm="unknown"'})
            account, user = user.split(':', 1)
            key = req.headers.get('x-auth-key')
            if not key:
                key = req.headers.get('x-storage-pass')
        else:
            return HTTPBadRequest(request=req)

        if not all((account, user, key)):
            self.logger.increment('token_denied')
            realm = account or 'unknown'
            return HTTPUnauthorized(request=req, headers={'Www-Authenticate':
                                                          'Swift realm="%s"' %
                                                          realm})
        
        query="AUTH:%s:%s:%s"%(account,user,key)
        self.socket.send(query)
        res=self.socket.recv()
        if res=="DENY":
            return HTTPUnauthorized(request=req, headers={'Www-Authenticate':'Swift realm="unknown"'})
        
        account_user = account + ':' + user #account user= "test:tester"
        url=res
        account_id = url.rsplit('/', 1)[-1]
        token=None
        if not token:
            # Generate new token
            logger.info("get rep %s %s %s"%(account_id,self.reseller_prefix,self.token_life))
            token = '%stk%s' % (self.reseller_prefix, uuid4().hex)
            expires = time() + self.token_life
            k="%s,%s,%s"%(account, account_user, account_id)
            self.cache[token]=k
            
        resp = Response(request=req, headers={
            'x-auth-token': token, 'x-storage-token': token})
        #replace here
        url = url.replace('$HOST', resp.host_url)
        if self.storage_url_scheme != 'default':
            url = self.storage_url_scheme + ':' + url.split(':', 1)[1]
        resp.headers['x-storage-url'] = url
        #http://127.0.0.1:8080/v1/AUTH_test
        return resp


def filter_factory(global_conf, **local_conf):
    """Returns a WSGI filter app for use with paste.deploy."""
    conf = global_conf.copy()
    conf.update(local_conf)

    def auth_filter(app):
        return TempAuth(app, conf)
    return auth_filter
